package com.pfdu.simplescheduler.repo;

import com.pfdu.simplescheduler.common.Task;
import com.pfdu.simplescheduler.common.TaskStatus;
import com.pfdu.simplescheduler.config.SimpleSchedulerConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.math.BigInteger;
import java.time.LocalDateTime;
import java.util.*;

@Slf4j
@Component
public class TaskRepo {

    @Autowired
    @Qualifier(value = "SimpleSchedulerJdbcTemplate")
    private JdbcTemplate jdbcTemplate;

    @Autowired
    private SimpleSchedulerConfig config;

    private String table;

    @PostConstruct
    public void init() {
        this.table = config.getTaskTable();
    }


    /**
     * 查出未启动的 Task
     * 保证先产生的任务先执行，后产生的任务后执行。
     *
     * @return
     */
    public List<Task> listNotStarted() {
        String sql = "select ROWNUM, tmp.* from (" +
                " select id, status, version, create_time AS createTime, update_time AS updateTime, content, type" +
                " from " + table +
                " where status = 0" +
                " order by create_time ASC" +
                ") tmp" +
                " where ROWNUM < 100";
        return jdbcTemplate.query(sql, new BeanPropertyRowMapper(Task.class));
    }

    /**
     * 以乐观锁机制更新 Task
     *
     * @param task
     * @return
     */
    public int updateWithVersion(Task task) {
        StringBuilder sql = new StringBuilder();
        sql.append("update " + table + " set version = version + 1");
        List<Object> args = new ArrayList<>(4);
        TaskStatus status = task.getStatus();
        if (status != null) {
            sql.append(",status = ?");
            args.add(status.getId());
        }
        LocalDateTime updateTime = task.getUpdateTime();
        if (updateTime != null) {
            sql.append(",update_time = ?");
            args.add(updateTime);
        }
        sql.append(" where id = ? and version = ?");
        args.add(task.getId());
        args.add(task.getVersion());
        return jdbcTemplate.update(sql.toString(), args.toArray());
    }

    public Task get(BigInteger id) {
        String sql = "select id, status, version, create_time AS createTime, update_time AS updateTime, content, type" +
                " from " + table +
                " where id = ?";
        return jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Task.class), id);
    }

    public List<Task> listTimeoutTasks(int outTime) {
        String sql = "select id, status, version, create_time AS createTime, update_time AS updateTime, content, type" +
                " from " + table +
                " where (status = 2 or status = 1)" +
                " and update_time < (SYSDATE-NUMTODSINTERVAL(?, 'second'))";
        return jdbcTemplate.query(sql, new BeanPropertyRowMapper(Task.class), outTime);
    }

    public List<Task> listCanClean() {
        String sql = "select ROWNUM, tmp.* from (" +
                " select id" +
                " from " + table +
                " where status = ? and update_time < (SYSDATE-NUMTODSINTERVAL(?, 'day'))" +
                " ) tmp" +
                " where ROWNUM <= 100";
        return jdbcTemplate.query(sql, new BeanPropertyRowMapper(Task.class), TaskStatus.FINISH.getId(), config.getFinishedTaskExistDuration());
    }

    public void deleteExpired(List<Task> taskList) {
        if (taskList == null || taskList.size() < 1)
            return;

        StringBuilder sql = new StringBuilder();
        sql.append("delete from ")
                .append(table)
                .append(" where id in (");
        taskList.forEach(task -> sql.append(task.getId()).append(","));
        sql.deleteCharAt(sql.lastIndexOf(","));
        sql.append(")");
        jdbcTemplate.update(sql.toString());
    }

}
